import wx
import serial
import serial.tools.list_ports
import logging
import threading
import time
import datetime
import json
from enum import Enum


logging.basicConfig(
    format='%(asctime)s : %(levelname)s: %(message)s',
    level=logging.DEBUG)


def scanPortNames():
    port_list = list(serial.tools.list_ports.comports())
    logging.debug(port_list)

    names = []
    for p in port_list:
        name = p.device + ':' + p.description
        names.append(name)
    logging.debug(names)
    return names


class Status(Enum):
    unconnect = 0
    connected = 1
    connecting = 2


class Connection():
    def __init__(self):
        self.status = Status.unconnect
        self.baudrate = 4096
        self.port = ''
        self.onReceivedListener = None
        self.run = False
        self.timeout = 0.05

    def connect(self):
        try:
            self.connection = serial.Serial(
                self.port, self.baudrate, timeout=self.timeout)
            self.run = True
            self.receivingThread = threading.Thread(target=self.receiving)
            self.receivingThread.setDaemon(True)
            self.receivingThread.start()
            self.status = Status.connected
        except Exception:
            wx.MessageBox(
                '无法打开串口', '错误',
                wx.OK)

    def disconnect(self):
        if self.status != Status.connected:
            return

        self.run = False
        self.status = Status.unconnect
        self.connection.close()

    def writeBytes(self, b):
        logging.debug(b)
        self.connection.write(b)

    def receiving(self):
        while self.run:
            data = self.connection.readall()
            if not data:
                continue

            if self.onReceivedListener:
                self.onReceivedListener(data)
        logging.debug('exit receiving')

    def getStatusText(self):
        s = self.port + ' '
        if self.status == Status.connected:
            s += '已连接 '
        else:
            s += '已关闭 '

        s += str(self.baudrate) + 'bps '

        return s


class DataPack:
    __FLAG = ['收←◆', '发→◇']

    def __init__(self, data, timestamp=0, flag=0):
        if not timestamp:
            timestamp = datetime.datetime.now()

        self.timestamp = timestamp
        self.data = data
        self.flag = flag

    def toHex(data):
        s = ''
        for i in data:
            s += '%02x ' % (i)
        return s

    def fromHex(data):
        b = bytearray()
        data = data.strip().split(' ')
        logging.debug(data)

        for i in data:
            b.append(int(i, 16))

        return b

    def toString(self, addStamp=True, hexDisplay=False):
        s = ''
        if addStamp:
            s += self.timestamp.strftime('[%H:%M:%S.%f]')
            s += self.__FLAG[self.flag]
        if hexDisplay:
            s += DataPack.toHex(self.data)
        else:
            s += self.data.decode()

        if addStamp:
            s += '\n'

        return s


class ListCtrlComboPopup(wx.ComboPopup):
    def __init__(self):
        wx.ComboPopup.__init__(self)
        self.lc = None

    def AddItem(self, txt):
        self.lc.InsertItem(self.lc.GetItemCount(), txt)

    def OnMotion(self, evt):
        item, flags = self.lc.HitTest(evt.GetPosition())
        if item >= 0:
            self.lc.Select(item)
            self.curitem = item

    def OnLeftDown(self, evt):
        self.value = self.curitem
        self.Dismiss()

    def Init(self):
        self.value = -1
        self.curitem = -1

    def Create(self, parent):
        self.lc = wx.ListCtrl(
            parent,
            style=wx.LC_LIST | wx.LC_SINGLE_SEL)
        self.lc.Bind(wx.EVT_MOTION, self.OnMotion)
        self.lc.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        return True

    def GetControl(self):
        return self.lc

    def SetStringValue(self, val):
        idx = self.lc.FindItem(-1, val)
        if idx != wx.NOT_FOUND:
            self.lc.Select(idx)

    def GetStringValue(self):
        if self.value >= 0:
            return self.lc.GetItemText(self.value)
        return ""

    def OnPopup(self):
        wx.ComboPopup.OnPopup(self)

    def OnDismiss(self):
        wx.ComboPopup.OnDismiss(self)

    def PaintComboControl(self, dc, rect):
        wx.ComboPopup.PaintComboControl(self, dc, rect)

    def OnComboKeyEvent(self, event):
        wx.ComboPopup.OnComboKeyEvent(self, event)

    def OnComboDoubleClick(self):
        wx.ComboPopup.OnComboDoubleClick(self)

    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
        return wx.ComboPopup.GetAdjustedSize(self, minWidth,
                                             prefHeight, maxHeight)

    def LazyCreate(self):
        return wx.ComboPopup.LazyCreate(self)

    def AddList(self, l):
        for val in l:
            self.lc.InsertItem(self.lc.GetItemCount(), val)


class ListComboBox(wx.ComboCtrl):
    def __init__(self, parent, id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0,
                 validator=wx.DefaultValidator,
                 name=wx.ComboBoxNameStr, choices=[]):
        wx.ComboBox.__init__(
            self, parent, id, value, pos, size, style,
            validator, name)
        self.lp = ListCtrlComboPopup()
        self.SetPopupControl(self.lp)
        self.lp.AddList(choices)

        maxChar = 0
        for i in choices:
            if len(i) > maxChar:
                maxChar = len(i)
        logging.debug(self.GetCharHeight())
        self.SetPopupMaxHeight(self.GetCharHeight() * (len(choices) + 2))
        self.SetPopupMinWidth(self.GetCharWidth() * maxChar)


class ListView(wx.ScrolledWindow):
    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.VSCROLL,
                 name=wx.PanelNameStr):
        wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name)
        self.SetScrollbars(1, 1, 1, 1)

        sizer = wx.BoxSizer(wx.VERTICAL)

        self.SetSizer(sizer)

    def addItem(self, item):
        sizer = self.GetSizer()
        sizer.Add(item)


class SideList(ListView):
    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.VSCROLL,
                 name=wx.PanelNameStr):
        ListView.__init__(self, parent, id, pos, size, style, name)
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.onTimer)
        self.currentItem = None

    def repeatSend(self, repeat=True, send=None):
        if not repeat:
            self.timer.Stop()
            return

        self.itemList = self.GetChildren()
        self.itemList = sorted(self.itemList, key=SideList.cmp)
        self.itemIndex = 0
        self.Send = send
        self.timer.Start(int(
            self.itemList[self.itemIndex].sendIntervalTC.GetValue()))

    def cmp(item):
        return int(item.orderTC.GetValue())

    def onTimer(self, event):
        if self.currentItem is not None:
            self.currentItem.sendBtn.SetForegroundColour(wx.BLACK)

        self.currentItem = self.itemList[self.itemIndex]
        self.currentItem.sendBtn.SetForegroundColour(wx.Colour(255, 0, 0, 0))

        self.Send(self.currentItem.content.GetValue(),
                  self.currentItem.hexSendCB.IsChecked())

        self.itemIndex += 1
        self.itemIndex = int(self.itemIndex % len(self.itemList))
        self.timer.Start(int(
            self.itemList[self.itemIndex].sendIntervalTC.GetValue()))


class PresendItem(wx.Panel):
    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.TAB_TRAVERSAL,
                 name=wx.PanelNameStr):

        self.itemId = len(parent.GetChildren())
        wx.Panel.__init__(self, parent, id, pos, size, style, name)

        self.normalContent = 'hello world'
        self.hexContent = '00 01 02 03'

        self.autoSendCB = wx.CheckBox(self)
        self.content = wx.TextCtrl(self, size=(300, 28))
        self.content.SetValue(self.normalContent)
        self.Bind(wx.EVT_TEXT, self.onText, self.content)
        self.hexSendCB = wx.CheckBox(self)
        self.Bind(wx.EVT_CHECKBOX, self.onChecked, self.hexSendCB)
        self.orderTC = wx.TextCtrl(self, value='1', size=(25, 28))
        self.sendIntervalTC = wx.TextCtrl(self, value='1000', size=(50, 28))
        self.sendBtn = wx.Button(self, label='发送' + str(self.itemId))

        sizer = wx.BoxSizer(wx.HORIZONTAL)

        sizer.Add(
            self.autoSendCB,
            wx.SizerFlags().Center())
        sizer.Add(
            self.content,
            wx.SizerFlags().Expand().Border(
                wx.BOTTOM | wx.RIGHT, 2))
        sizer.Add(
            self.hexSendCB,
            wx.SizerFlags().Center())
        sizer.Add(
            self.orderTC)
        sizer.Add(
            self.sendIntervalTC)
        sizer.Add(
            self.sendBtn,
            wx.SizerFlags().Border(
                wx.RIGHT, 5))

        self.SetSizer(sizer)

    def setItemId(self, id):
        self.itemId = id

    def getItemId(self):
        return self.itemId

    def onChecked(self, event):
        if self.hexSendCB.IsChecked():
            self.content.SetValue(self.hexContent)
        else:
            self.content.SetValue(self.normalContent)

    def onText(self, event):
        if self.hexSendCB.IsChecked():
            self.hexContent = self.content.GetValue()
        else:
            self.normalContent = self.content.GetValue()


class NotifyWindow(wx.PopupWindow):
    def __init__(self, parent, flags=wx.BORDER_NONE):
        wx.PopupWindow.__init__(self, parent, flags)

        self.notifyLable = wx.StaticText(self, label='hello world')
        self.ReRect()

        self.SetBackgroundColour(wx.Colour(145, 200, 136, 255))

        self.timer = wx.Timer(self)

        self.Bind(wx.EVT_TIMER, self.OnTimer)

    def ReRect(self):
        parentRect = self.GetParent().GetRect()
        rect = self.notifyLable.GetRect().Inflate(
            20, 10).CenterIn(parentRect)
        self.SetRect(rect)
        self.notifyLable.CenterOnParent()

    def Show(self, show=True, label='None'):
        wx.PopupWindow.Show(self, show)
        self.timer.Start(3000, wx.TIMER_ONE_SHOT)
        self.notifyLable.SetLabel(label)
        self.ReRect()

    def OnTimer(self, event):
        self.Show(False)


class MainFrame(wx.Frame):
    __BAUDRATE_LIST = ['115200', '9600', '2400']
    SEND_PANEL_WIDTH = 600
    PARAM_FILE_NAME = 'param.json'

    def __init__(self, *args, **kw):
        super(MainFrame, self).__init__(*args, **kw)

        self.dataInit()

        self.panel = wx.Panel(self)
        # self.panel.SetBackgroundColour(wx.Colour(0, 1, 0))

        self.st = wx.TextCtrl(
            self.panel,
            style=wx.TE_MULTILINE | wx.TE_READONLY)
        font = self.st.GetFont()
        font.PointSize += 10
        font = font.Bold()
        self.st.SetFont(font)

        self.bottomLabel = wx.StaticText(self.panel, label='Hello World')
        self.clearButton = wx.Button(self.panel, label='清除')
        self.Bind(wx.EVT_BUTTON, self.OnClearButtonClick, self.clearButton)
        self.port_list = scanPortNames()
        self.port_name_box = ListComboBox(
            self.panel, choices=self.port_list, style=wx.CB_READONLY)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, self.OnPortChange,
                  self.port_name_box)
        self.con_status_label = wx.StaticText(self.panel, label='未连接')
        self.connectButton = wx.Button(self.panel, label='打开串口')
        self.Bind(wx.EVT_BUTTON, self.OnConnectButtonClick, self.connectButton)
        self.baudrateBox = wx.ComboBox(
            self.panel, choices=self.__BAUDRATE_LIST,
            value=self.__BAUDRATE_LIST[0], style=wx.CB_READONLY)
        self.Bind(wx.EVT_COMBOBOX, self.OnBaudrateChange, self.baudrateBox)

        self.tlabel1 = wx.StaticText(self.panel, label='Hello World')
        self.hexDisplayCB = wx.CheckBox(self.panel, label='HEX显示')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.hexDisplayCB)
        self.stampCB = wx.CheckBox(self.panel, label='显示时间戳和分包')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.stampCB)
        self.hexSendCB = wx.CheckBox(self.panel, label='HEX发送')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.hexSendCB)
        self.autoSendCB = wx.CheckBox(self.panel, label='自动发送')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.autoSendCB)
        self.sendIntervalTC = wx.TextCtrl(self.panel, value='1000')
        self.Bind(wx.EVT_TEXT, self.OnTextChanged, self.sendIntervalTC)
        self.sidebarCB = wx.CheckBox(self.panel, label='多字符串发送')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.sidebarCB)
        self.sendtc = wx.TextCtrl(
            self.panel, style=wx.TE_MULTILINE, size=(100, 100))
        self.sendtc.write('hello world')
        self.Bind(wx.EVT_TEXT, self.OnTextChanged, self.sendtc)
        self.sendButton = wx.Button(self.panel, label='发送')
        self.Bind(wx.EVT_BUTTON, self.OnSendButtonClick, self.sendButton)

        self.sidePanel = wx.Panel(self.panel)
        self.sidePanelLable = wx.StaticText(self.sidePanel, label='多条字符串发送')
        self.repeatSendCB = wx.CheckBox(self.sidePanel, label='循环发送')
        self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.repeatSendCB)
        self.presendLV = SideList(
            self.sidePanel, size=(self.SEND_PANEL_WIDTH, 20))
        self.makeItems()
        self.sidePanel.Show(False)

        sizer11 = wx.BoxSizer(wx.VERTICAL)
        sizer11.Add(
            self.bottomLabel,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))
        sizer11.Add(
            self.clearButton,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))
        sizer11.Add(
            self.port_name_box,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))
        sizer11.Add(
            self.con_status_label,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))
        sizer11.Add(
            self.connectButton,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))
        sizer11.Add(
            self.baudrateBox,
            wx.SizerFlags().Border(wx.LEFT | wx.TOP, 5))

        sizer12 = wx.BoxSizer(wx.VERTICAL)
        sizer12.Add(
            self.tlabel1,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.hexDisplayCB,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.stampCB,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.hexSendCB,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.autoSendCB,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.sendIntervalTC,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.sidebarCB,
            wx.SizerFlags().Expand())
        sizer12.Add(
            self.sendtc,
            wx.SizerFlags().Proportion(1).Expand())

        sizer121 = wx.BoxSizer(wx.HORIZONTAL)
        sizer121.Add(
            self.sendButton,
            wx.SizerFlags())

        sizer12.Add(
            sizer121,
            wx.SizerFlags().Align(wx.ALIGN_RIGHT))

        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(
            sizer11,
            wx.SizerFlags())
        sizer1.Add(
            sizer12,
            wx.SizerFlags().Proportion(1))

        sizerMain = wx.BoxSizer(wx.VERTICAL)
        sizerMain.Add(
            self.st,
            wx.SizerFlags().Border(wx.TOP | wx.LEFT | wx.RIGHT, 5).
            Expand().
            Proportion(1))
        sizerMain.Add(
            sizer1,
            wx.SizerFlags().Expand().
            Border(wx.LEFT | wx.TOP | wx.RIGHT, 5))

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(
            self.repeatSendCB,
            wx.SizerFlags().Center())

        sideSizer = wx.BoxSizer(wx.VERTICAL)
        sideSizer.Add(
            self.sidePanelLable,
            wx.SizerFlags().Expand())
        sideSizer.Add(
            sizer,
            wx.SizerFlags().Expand())
        sideSizer.Add(
            self.presendLV,
            wx.SizerFlags().Expand().Proportion(1))

        self.sidePanel.SetSizer(sideSizer)

        sizer = wx.FlexGridSizer(2)
        sizer.AddGrowableCol(0)
        sizer.AddGrowableRow(0)
        sizer.Add(
            sizerMain,
            wx.SizerFlags().Expand())
        sizer.Add(
            self.sidePanel,
            wx.SizerFlags().Expand())

        self.panel.SetSizer(sizer)

        self.makeMenuBar()

        self.CreateStatusBar(3)
        statusBarWidth = [70, 70, -1]
        self.SetStatusWidths(statusBarWidth)
        self.updateStatusBar()

        self.notifyWindow = NotifyWindow(self)
        self.notifyWindow.Show()

        self.Bind(wx.EVT_MOVE, self.OnMove, self)

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer)

        self.loadParamFromJson()

    def dataInit(self):
        self.txCount = 0
        self.rxCount = 0
        self.dataList = []
        self.normalContent = 'hello world'
        self.hexContent = '00 01 02 03 04'

        self.connection = Connection()
        self.connection.onReceivedListener = self.OnReceived

    def makeItems(self):
        for i in range(20):
            item = PresendItem(self.presendLV)
            self.Bind(wx.EVT_BUTTON, self.OnButton, item.sendBtn)
            self.presendLV.addItem(item)

    def updateStatusBar(self):
        baudrate = int(self.baudrateBox.GetValue())
        self.connection.baudrate = baudrate

        port = self.port_name_box.GetValue().split(':')[0]
        self.connection.port = port

        self.SetStatusText("S:%d" % (self.txCount), 0)
        self.SetStatusText('R:%d' % (self.rxCount), 1)
        self.SetStatusText(self.connection.getStatusText(), 2)

    def updateUI(self):
        if self.connection.status == Status.connected:
            self.con_status_label.SetLabel('已连接')
            self.connectButton.SetLabel('关闭串口')
        else:
            self.con_status_label.SetLabel('未连接')
            self.connectButton.SetLabel('打开串口')

    def Send(self, data, hexSend=False):
        if self.connection.status != Status.connected:
            return

        sendData = data
        if hexSend:
            try:
                sendData = DataPack.fromHex(sendData)
            except Exception:
                sendData = bytearray()
                self.notifyWindow.Show(label='请输入16进制数据')
        else:
            sendData = sendData.encode()
        dataPack = DataPack(sendData, flag=1)
        self.dataList.append(dataPack)
        if self.stampCB.IsChecked():
            self.st.AppendText(dataPack.toString(
                True, self.hexDisplayCB.IsChecked()))

        self.connection.writeBytes(dataPack.data)
        self.txCount += len(dataPack.data)

        self.updateStatusBar()

    def saveParamAsJson(self):
        param = {'window_size': (
            self.GetSize().GetWidth(), self.GetSize().GetHeight())}

        logging.debug(param)

        file = open(self.PARAM_FILE_NAME, 'w')
        json.dump(param, file, indent=4)
        file.close()

    def loadParamFromJson(self):
        try:
            file = open(self.PARAM_FILE_NAME)
        except Exception:
            return

        params = json.load(file)
        logging.debug('load param: ' + str(params))

        file.close()

        self.SetSize(params['window_size'])

    def makeMenuBar(self):
        fileMenu = wx.Menu()
        helloItem = fileMenu.Append(
            -1,
            "&Hello...\tCtrl-H",
            "Help string shown in status bar for this menu item")
        fileMenu.AppendSeparator()

        exitItem = fileMenu.Append(wx.ID_EXIT)

        helpMenu = wx.Menu()
        aboutItem = helpMenu.Append(wx.ID_ABOUT)

        menuBar = wx.MenuBar()
        menuBar.Append(fileMenu, "&File")
        menuBar.Append(helpMenu, "&Help")

        self.SetMenuBar(menuBar)

        self.Bind(wx.EVT_MENU, self.OnHello, helloItem)
        self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
        self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)

        self.Bind(wx.EVT_CLOSE, self.OnClose, self)

    def OnClose(self, event):
        event.Skip()
        self.saveParamAsJson()
        logging.debug('close window')

    def OnExit(self, event):
        self.Close(True)

    def OnHello(self, event):
        wx.MessageBox("Hello again from wxPython")

    def OnAbout(self, event):
        wx.MessageBox(
            "This is a wxPython Hello World sample",
            "About Hello World 2",
            wx.OK | wx.ICON_INFORMATION)

    def OnMove(self, event):
        self.notifyWindow.ReRect()

    def OnSize(self, event):
        self.notifyWindow.ReRect()

    def OnConnectButtonClick(self, event):
        if self.connection.status == Status.connected:
            self.connection.disconnect()
        else:
            self.connection.connect()

        self.updateUI()
        self.updateStatusBar()

    def OnSendButtonClick(self, event):
        self.Send(self.sendtc.GetValue(), self.hexSendCB.IsChecked())

    def OnClearButtonClick(self, event):
        self.st.SetValue('')

    def OnBaudrateChange(self, event):
        self.updateStatusBar()

    def OnPortChange(self, event):
        self.updateStatusBar()

    def OnReceived(self, data):
        dataPack = DataPack(data)
        self.dataList.append(dataPack)
        self.st.AppendText(dataPack.toString(
            self.stampCB.IsChecked(), self.hexDisplayCB.IsChecked()))
        self.rxCount += len(data)
        logging.debug(data)
        self.updateStatusBar()

    def ConvertST(self):
        self.st.SetValue('')
        for data in self.dataList:
            if not self.stampCB.IsChecked() and (data.flag == 1):
                continue
            self.st.AppendText(data.toString(
                self.stampCB.IsChecked(), self.hexDisplayCB.IsChecked()))

    def ConvertSend(self):
        if self.hexSendCB.IsChecked():
            self.sendtc.SetValue(self.hexContent)
        else:
            self.sendtc.SetValue(self.normalContent)

    def OnChecked(self, event):
        cb = event.GetEventObject()

        if cb is self.hexDisplayCB:
            self.ConvertST()
        elif cb is self.stampCB:
            self.ConvertST()
        elif cb is self.hexSendCB:
            self.ConvertSend()
        elif cb is self.autoSendCB:
            if cb.IsChecked():
                self.timer.Start(int(self.sendIntervalTC.GetValue()))
            else:
                self.timer.Stop()
        elif cb is self.sidebarCB:
            self.sidePanel.Show(cb.IsChecked())
            # 子控件显示或隐藏之后需要调用父控件的Layout()方法更新UI
            self.panel.Layout()
        elif cb is self.repeatSendCB:
            if cb.IsChecked():
                self.presendLV.repeatSend(send=self.Send)
            else:
                self.presendLV.repeatSend(False)

    def OnTextChanged(self, event):
        tc = event.GetEventObject()

        if tc is self.sendIntervalTC:
            if self.autoSendCB.IsChecked():
                self.timer.Start((int(self.sendIntervalTC.GetValue())))
        elif tc is self.sendtc:
            if self.hexSendCB.IsChecked():
                self.hexContent = self.sendtc.GetValue()
            else:
                self.normalContent = self.sendtc.GetValue()

    def OnButton(self, event):
        btn = event.GetEventObject()
        item = btn.GetParent()
        logging.debug('发送' + str(item.getItemId()))
        self.Send(item.content.GetValue(), item.hexSendCB.IsChecked())

    def OnTimer(self, event):
        self.Send()


if __name__ == '__main__':
    app = wx.App()
    frame = MainFrame(None, title="transfer", size=(640, 480))
    frame.Show()
    app.MainLoop()
